#include "udpServer.hpp"
#include <memory>
#include <cstdio>
#include <fstream>
#include <unordered_map>
#include <signal.h>

using namespace std;
using namespace Server;

#define dictPath "./dict.txt"
#define SEP ":"
unordered_map<string, string> dict;

void Usage(const string &proc)
{
    // cout << "\nUsage:\n\t" << proc << " local_ip local_port\n" << endl;
    cout << "\nUsage:\n\t" << proc << " local_port\n" << endl;
}

static bool cutString(const string &str, string *key, string *value)
{
    string sep = SEP;
    auto pos = str.find(SEP);
    if(pos == string::npos) return false;
    *key = str.substr(0, pos);
    *value = str.substr(pos + sep.size());
    return true;
}

static void initdict()
{
    // 打开文件
    // ifstream in(dictPath);           // 这样写也可以，因为已经指定以读方式打开了
    ifstream in(dictPath, std::ios::binary);
    if (!in.is_open())
    {
        cerr << "open file error: " << dictPath << " -> " << strerror(errno) << endl;
        exit(OPENFILE_ERR);
    }

    string str;
    string key, value;
    while(getline(in, str))
    {
        if(cutString(str, &key, &value))
        {
            dict.insert(make_pair(key, value));
        }
        else
        {
            cerr << "cutString err" << endl;
            exit(CUT_ERR);
        }
    }
    in.close();
    cout << "load dict success! " << endl;
}

// 业务逻辑与通信逻辑解耦
void TranslateMessage(int sockfd, string clientIp, uint16_t clientPort, string message)
{
    string sendMsg;
    auto pos = dict.find(message);
    if(pos == dict.end())
    {
        sendMsg = "unknown!";
    }
    else
    {
        sendMsg = "Server Translate -> ";
        sendMsg += pos->second;
    }

    // 开始返回数据
    struct sockaddr_in client;
    bzero(&client, sizeof(client));

    client.sin_family = AF_INET;
    client.sin_port = htons(clientPort);
    client.sin_addr.s_addr = inet_addr(clientIp.c_str());

    sendto(sockfd, sendMsg.c_str(), sendMsg.size(), 0, (struct sockaddr*)&client, sizeof(client));
}

void reload(int signo)
{
    (void)signo;
    // 对2号信号做捕捉，实现字典的热加载(有点类似不停机更新)
    initdict();
}

void execCommand(int sockfd, string clientIp, uint16_t clientPort, string message)
{
    if(message.find("rm") != string::npos || message.find("rmdir") != string::npos ||message.find("mv") != string::npos)
    {
        cerr << clientIp << "[" << clientPort << "]" << "doing invalid cmd : " << message << endl;
        return;
    }

    // 实现远程命令(类似之前的myshell模拟实现)
    // pipe + fork + exec* = popen
    string sendMsg;
    FILE *fp = popen(message.c_str(), "r");     // 相当于把执行完的命令放到fp里，咱们以读方式打开fp文件读取执行结果
    if(nullptr == fp) sendMsg = message + " exec failed !";

    char line[1024];
    while(fgets(line, sizeof(line), fp))    // 第二个参数可减一，也可以不减，因为fgets会自动帮我们添加"\0"
    {
        sendMsg += line;
    }

    pclose(fp);

    // 开始返回数据
    struct sockaddr_in client;
    bzero(&client, sizeof(client));

    client.sin_family = AF_INET;
    client.sin_port = htons(clientPort);
    client.sin_addr.s_addr = inet_addr(clientIp.c_str());
    
    sendto(sockfd, sendMsg.c_str(), sendMsg.size(), 0, (struct sockaddr*)&client, sizeof(client));
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }

    signal(2, reload);

    uint16_t port = atoi(argv[1]);
    // string ip = argv[1];

    // unique_ptr<udpServer> usvr(new udpServer(port, ip));

    // unique_ptr<udpServer> usvr(new udpServer(TranslateMessage, port));
    unique_ptr<udpServer> usvr(new udpServer(execCommand, port));

    initdict();     // 要启动服务器就得加载了，服务器是个死循环！

    usvr->initServer();
    usvr->start();

    return 0;
}